using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using SPALM.SharePointSoftwareFactory.SetupHelper.Properties;
using System.Globalization;
using System.Resources;
using System.Collections;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using System.Drawing.Imaging;

namespace SPALM.SharePointSoftwareFactory.SetupHelper.SPSFFiles
{
  public partial class MSIFile : UserControl
  {
    //private MSIContainer _parent;
    private string title;
    private string description;
    private string productGuid;
    private string regKey;
    private string checkFileExists;
    private string setupFilename;
    private string setupDir;
    private string vsVersion;

    public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;
    public const int READ_CONTROL = 0x00020000;
    public const int SYNCHRONIZE = 0x00100000;
    public const int STANDARD_RIGHTS_READ = READ_CONTROL;
    public const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
    public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;
    public const int STANDARD_RIGHTS_ALL = 0x001F0000;
    public const int KEY_QUERY_VALUE = 0x0001;
    public const int KEY_SET_VALUE = 0x0002;
    public const int KEY_CREATE_SUB_KEY = 0x0004;
    public const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
    public const int KEY_NOTIFY = 0x0010;
    public const int KEY_CREATE_LINK = 0x0020;
    public const int KEY_WOW64_32KEY = 0x0200;
    public const int KEY_WOW64_64KEY = 0x0100;

    private bool uninstall;

    public const int KEY_ALL_ACCESS = STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK & (~SYNCHRONIZE);

    public MSIFile(MSIContainer _parent, bool _uninstall, string _setupDir, string _title, string _description, string _productGuid, string _regKey, string _setupFilename, string _vsVersion, string _checkFileExists)
    {
      InitializeComponent();

      title = _title;
      description = _description;
      productGuid = _productGuid;
      regKey = _regKey;
      checkFileExists = _checkFileExists;
      setupFilename = _setupFilename;
      setupDir = _setupDir;
      vsVersion = _vsVersion;

      label1.Text = title;
      labelDescription.Text = description;

      uninstall = _uninstall;

      if (uninstall)
      {
        buttonInstall.Text = "Uninstall";
        this.Visible = false; //erstmal unsichtbar, nur wenn es installiert ist, dann anzeigen
      }

      CheckInstallation();

    }

    private string GetTempInstallationPath()
    {
      if (this.ParentForm is Form1)
      {
        return (this.ParentForm as Form1).GetTempInstallerPath();
      }
      return "";
    }

    private void buttonInstall_Click(object sender, EventArgs e)
    {
      if (uninstall)
      {
        if (setupFilename.EndsWith(".vsix", StringComparison.InvariantCultureIgnoreCase))
        {
          VSIXUninstall(this.productGuid);
        }
        else if (setupFilename.EndsWith(".msi", StringComparison.InvariantCultureIgnoreCase))
        {
          MSIUninstall(this.productGuid);
        }
        else if (setupFilename.EndsWith(".mshc", StringComparison.InvariantCultureIgnoreCase))
        {
          HelpUninstall(this.productGuid);
        }
      }
      else
      {
        //find the file setupFilename in the temp directory
        string tempDir = GetTempInstallationPath();
        string fullsetupFilename = Path.Combine(tempDir, setupFilename);
        if (!File.Exists(fullsetupFilename))
        {
          MessageBox.Show("Setupfile with name " + setupFilename + " not found");
        }
        else
        {
          if (fullsetupFilename.EndsWith(".vsix", StringComparison.InvariantCultureIgnoreCase))
          {
            VSIXInstall(fullsetupFilename);
          }
          else if (setupFilename.EndsWith(".msha", StringComparison.InvariantCultureIgnoreCase))
          {
            HelpInstall(fullsetupFilename);
          }
          else
          {
            MSIInstall(fullsetupFilename);            
          }
        }
      }
    }

    private void HelpInstall(string fullsetupFilename)
    {
      try
      {
        if (File.Exists(fullsetupFilename))
        {
          StartProcess(GetPathToHelpLibManager(), "/product VS /version 100 /locale en-US /install /sourceMedia \"" + fullsetupFilename + "\"", false);
        }
        else
        {
          MessageBox.Show("File not found: " + fullsetupFilename);
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }
   
    private void MSIUninstall(string productID)
    {
      try
      {
        string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "msiexec.exe");
        if (File.Exists(path))
        {
          StartProcess(path, "/x " + productID, false);
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }

    private void MSIInstall(string fullsetupFilename)
    {
      try
      {
        StartProcess(fullsetupFilename, "", true);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }

    private string GetPathToHelpLibManager()
    {
      RegistryKey startupKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Help\V1.0", true);
      if (startupKey != null)
      {
        string pathToHelpExe = startupKey.GetValue("AppRoot").ToString();
        pathToHelpExe = Path.Combine(pathToHelpExe, "HelpLibManager.exe");
        if (File.Exists(pathToHelpExe))
        {
          return pathToHelpExe;
        }
      }
      return "";
    }

    private void StartVSIXProcess(string programPath, string arguments)
    {
      //MessageBox.Show("Please wait while the Microsoft VSIX Installer loads. This can take up to a minute.");
      StartProcess(programPath, arguments, false);
    }

    private void StartProcess(string programPath, string arguments, bool UseShellExecute)
    {
      if (File.Exists(programPath))
      {
        ProcessStartInfo psi = new ProcessStartInfo();
        psi.FileName = programPath;
        if (!string.IsNullOrEmpty(arguments))
        {
          psi.Arguments = arguments;
        }
        psi.UseShellExecute = UseShellExecute;
        runningProcess = new System.Diagnostics.Process();
        runningProcess.StartInfo = psi;
        runningProcess.Start();
        StartTimer();
      }
      else
      {
        Console.WriteLine("Program does not exist");
        Console.ReadLine();
      }
    }

    private void HelpUninstall(string prodid)
    {
      try
      {
        StartProcess(GetPathToHelpLibManager(), "/product VS /version 100 /locale en-US /uninstall " + prodid, false);
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }

    Process runningProcess = null;
    private void VSIXInstall(string fullsetupFilename)
    {
      //
      try
      {
        string path = Path.Combine(Helpers.GetInstallationFolderOfVS2010(), "VSIXInstaller.exe");
        if (File.Exists(path))
        {
          StartVSIXProcess(path, "/admin \"" + fullsetupFilename + "\"");
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }

    private void VSIXUninstall(string productID)
    {
      //
      try
      {
        string arguments = "/uninstall:" + productID;
        if (vsixInstalledForAllUsers)
        {
          arguments = "/admin " + arguments;
        }
        string path = Path.Combine(Helpers.GetInstallationFolderOfVS2010(), "VSIXInstaller.exe");
        if (File.Exists(path))
        {
           StartVSIXProcess(path, arguments);
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show("Error: " + ex.Message);
      }
    }

    void process_Exited(object sender, EventArgs e)
    {

      CheckInstallation();
    }

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern int RegDisableReflectionKey(IntPtr hBase);

    private bool vsixInstalledForAllUsers = false;

    public void CheckInstallation()
    {
      if (IsProcessRunning())
      {
        return;
      }

      SetInstalled(false);
      this.pictureBox1.Visible = true;

      if (!string.IsNullOrEmpty(checkFileExists))
      {
        if (!checkFileExists.StartsWith(@"\"))
        {
          checkFileExists = @"\" + checkFileExists;
        }

        string userData = GetExtensionsFolder() + checkFileExists;
        if (File.Exists(userData))
        {
          vsixInstalledForAllUsers = true;
          SetInstalled(true);
          return;
        }

        //search for the file in different locations
        //2. C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Extensions
        //1. applocaldata //Microsoft\VisualStudio\10.0\Extensions\
        string appLocalData1 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\VisualStudio\10.0\Extensions\" + checkFileExists);
        if (File.Exists(appLocalData1))
        {
          SetInstalled(true);
          return;
        }

        try
        {

          FieldInfo hkeyInfo = GetHkeyField();

          RegistryKey remoteKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, Environment.MachineName);
          SafeHandle remoteKeyHandle = (SafeHandle)hkeyInfo.GetValue(remoteKey);
          IntPtr hRemoteKey = remoteKeyHandle.DangerousGetHandle();
          IntPtr hTargetKey = IntPtr.Zero;
          int l = RegOpenKeyEx(hRemoteKey, @"SOFTWARE\Microsoft\Help\v1.0\", 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, out hTargetKey);

          SafeHandle sh = CreateRegistrySafeHandle(hTargetKey);
          RegistryKey helpKey = (RegistryKey)Activator.CreateInstance(typeof(RegistryKey), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { sh, true }, null);

          if (helpKey.GetValue("LocalStore") != null)
          {
            string localStore = helpKey.GetValue("LocalStore").ToString();
            if (!localStore.EndsWith("\\"))
            {
              localStore += "\\";
            }
            string checkPath = localStore + checkFileExists;
            if (File.Exists(checkPath))
            {
              SetInstalled(true);
              return;
            }
          }
        }
        catch { }

        //search in folder of help 
      }
      else if (!string.IsNullOrEmpty(regKey))
      {
        //
        //checks the installation of the software

        //
        string registationKey1 = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + regKey;

        if (registationKey1.Length > 0 && Registry.LocalMachine.OpenSubKey(registationKey1) != null)
        {
          SetInstalled(true);
          return;
        }

        //The registry key:
        string SoftwareKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
        using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(SoftwareKey))
        {
          //Let's go through the registry keys and get the info we need:
          foreach (string skName in rk.GetSubKeyNames())
          {
            using (RegistryKey sk = rk.OpenSubKey(skName))
            {
              try
              {
                //If the key has value, continue, if not, skip it:
                if (!(sk.GetValue("DisplayName") == null))
                {
                  if (sk.GetValue("DisplayName").ToString().StartsWith(productGuid))
                  {
                    SetInstalled(true);
                  }
                }
              }
              catch (Exception)
              {
              }
            }
          }
        }


        //windows server 2008
        //folder C:\Windows\Installer\{ED3B3970-2FE1-48A2-9787-CA639B8CFA17}\ exists
        string installFolder = Path.Combine(@"C:\Windows\Installer", regKey);
        if (Directory.Exists(installFolder))
        {
          SetInstalled(true);
        }
      }
      else
      {
        //no check for installation given, we hide the image
        this.pictureBox1.Visible = false;
      }
    }

    private string GetExtensionsFolder()
    {
      string result = Path.Combine(Helpers.GetInstallationFolderOfVS2010(), "Extensions");
      return result;
    }

    private static SafeHandle CreateRegistrySafeHandle(IntPtr handle)
    {
      Assembly ass = typeof(SafeHandle).Assembly;
      Type type = ass.GetType("Microsoft.Win32.SafeHandles.SafeRegistryHandle");
      SafeHandle sh = (SafeHandle)Activator.CreateInstance(type, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { handle, true }, null);
      return sh;
    }


    private static FieldInfo GetHkeyField()
    {
      Type type = typeof(RegistryKey);
      FieldInfo info = type.GetField("hkey", BindingFlags.NonPublic | BindingFlags.Instance);
      return info;
    }


    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "RegOpenKeyEx")]
    static extern int RegOpenKeyEx(
        IntPtr hKey,
        string subKey,
        uint options,
        int sam,
        out IntPtr phkResult);


    private void SetInstalled(bool installed)
    {
      if (uninstall)
      {
        this.pictureBox1.Image = imageList1.Images[2];
        if (installed)
        {
          this.Visible = true;
        }
        else
        {
          //if not installed, then do not display the element
          this.Visible = false;
        }
      }
      else
      {
        if (installed)
        {
          this.pictureBox1.Image = imageList1.Images[1];
          //this.buttonInstall.Text = "Uninstall";
        }
        else
        {
          this.pictureBox1.Image = imageList1.Images[0];
          //this.buttonInstall.Text = "Install";
        }
      }
    }

    internal void SetVSVersions(bool vs2008, bool vs2010)
    {
      this.Visible = false;
      if (string.IsNullOrEmpty(vsVersion))
      {
        this.Visible = true;
        return;
      }
      if (vs2008)
      {
        if (this.vsVersion == "2008")
        {
          this.Visible = true;
        }
      }
      if (vs2010)
      {
        if (this.vsVersion == "2010")
        {
          this.Visible = true;
        }
      }
    }

    bool IsProcessRunning()
    {
      if((runningProcess != null) && (runningProcess.HasExited == false))
      {
        return true;
      }
      return false;
    }

    private void StartTimer()
    {
      this.Cursor = Cursors.WaitCursor;
      this.buttonInstall.Enabled = false;
      this.pictureBox1.Image = global::SPALM.SharePointSoftwareFactory.SetupHelper.Properties.Resources.loading;
      this.timer1.Enabled = true;
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
      if (IsProcessRunning())
      {
        //process is running
        this.buttonInstall.Enabled = false;
      }
      else
      {
        this.Cursor = Cursors.Default;
        this.timer1.Enabled = false;
        this.buttonInstall.Enabled = true;
        CheckInstallation();
        if (uninstall)
        {
        }
        else
        {
          if (setupFilename.EndsWith(".vsix", StringComparison.InvariantCultureIgnoreCase))
          {
            //folder where our extension is installed
            string userData = GetExtensionsFolder() + checkFileExists;
            AddDirectorySecurity(userData, @"Everyone", FileSystemRights.Modify, AccessControlType.Allow);
          }
        }
      }
    }

    private void RemoveReadOnly(DirectoryInfo startDirectory)
    {
      startDirectory.Attributes = System.IO.FileAttributes.Normal & ~System.IO.FileAttributes.ReadOnly;
      foreach(FileInfo fInfo in startDirectory.GetFiles())
      {
        try
        {
          fInfo.Attributes = System.IO.FileAttributes.Normal & ~System.IO.FileAttributes.ReadOnly;
        }
        catch { }
      }
      foreach (DirectoryInfo dInfo in startDirectory.GetDirectories())
      {
        RemoveReadOnly(dInfo);
      }
    }

    // Adds an ACL entry on the specified directory for the specified account.
    public void AddDirectorySecurity(string FileName, string Account, FileSystemRights Rights, AccessControlType ControlType)
    {
      try
      {
        DirectoryInfo dInfo = null;
        //is it a file or directory?
        FileAttributes attr = File.GetAttributes(FileName);
        //detect whether its a directory or file
        if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
        {
          //is directory
          dInfo = new DirectoryInfo(FileName);
        }
        else
        {
          //no directory
          FileInfo fInfo = new FileInfo(FileName);
          dInfo = fInfo.Directory;
        }

        // Create a new DirectoryInfo object.
        RemoveReadOnly(dInfo);

        // Get a DirectorySecurity object that represents the 
        // current security settings.
        DirectorySecurity dSecurity = dInfo.GetAccessControl();

        // Add the FileSystemAccessRule to the security settings. 
        dSecurity.AddAccessRule(new FileSystemAccessRule(Account, Rights, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, ControlType));

        // Set the new access settings.
        dInfo.SetAccessControl(dSecurity);
      }
      catch
      {
      }
    }
  }
}
